package exam2006jan;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Loader;
import javassist.NotFoundException;


public class Main {

	public static void main(String[] args) {

		ClassPool cp = ClassPool.getDefault();
		CtClass a = cp.makeClass("a");
		CtMethod f = null;
		CtMethod g = null;
		try {
			f = CtNewMethod.make("public int f(int x){ return x+1;}", a);
			g = CtNewMethod.make("public int g(int x){ return x+2;}", a);
			a.addMethod(f);
			a.addMethod(g);
			a.writeFile();
		} catch (CannotCompileException e) {
			e.printStackTrace();
		} catch (NotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//compose f g
		CtMethod fg = compose(f,g);
		a.defrost();
		try {
			a.addMethod(fg);
			a.writeFile();
		} catch (CannotCompileException e) {
			e.printStackTrace();
		} catch (NotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		ClassPool pool = ClassPool.getDefault();
		Loader cl = new Loader ( pool ) ;
		try {
			Class c = cl.loadClass("a");
			Object aa = c.newInstance();
			Method fgM = aa.getClass().getMethod("fg", int.class);
			Integer args1 = new Integer(0);
			Object res = fgM.invoke(aa, args1);
			System.out.println(res);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		
		
		plusPetitAncetreCommun(new Object[]{new C(), new D()});
		
	}
	
	public static CtMethod compose(CtMethod f, CtMethod g){
		
		try {
			if(f.getParameterTypes().length>1){
				System.out.println("IllegalCompositionException");
				return null;
			}
		} catch (NotFoundException e1) {
			e1.printStackTrace();
		}
		CtMethod fg = null;
		
		String fgBody = "";
		
		
		try {
			f.getDeclaringClass().defrost();
			fg = new CtMethod(f.getReturnType(),"fg",g.getParameterTypes(),f.getDeclaringClass());
			
			fgBody = " return "+f.getName()+"("+g.getName()+"(";
			int i;
			for(i=1; i<g.getParameterTypes().length;i++)
				fgBody+="$"+i+",";
			fgBody+="$"+i+"));";
			
			fg.setBody(fgBody);
			
			
		} catch (NotFoundException e) {
			e.printStackTrace();
		} catch (CannotCompileException e) {
			e.printStackTrace();
		}
		return fg;
	}
	
	public static Class plusPetitAncetreCommun(Object[] tab){
		
		if(tab.length==0)
			return null;
		
		ArrayList<ArrayList<Class>> classes = new ArrayList<ArrayList<Class>>();
		
		int ind=0;
		for(Object o : tab){
			ArrayList<Class> temp = new ArrayList<Class>();
			Class superClass = o.getClass().getSuperclass();
			System.out.println(o.getClass().getName());
			
			while(!superClass.getName().equals("java.lang.Object")){
				System.out.println(superClass.getName());
				temp.add(superClass);
				superClass = superClass.getSuperclass();
			}
			classes.add(ind, temp);
			
		}
		
		
		
		return null;
	}
	
}
